<?php
/**
 * Pdo.class.php            PDO类
 * 
 * @author                  mashanling(msl-138@163.com)
 * @date                    2012-03-23 18:04:05
 * @last modify             2012-03-23 18:04:05 by mashanling
 */

!defined('ROOT') && exit('Access Denied');

class DB_PDO extends PDO {
    private $statement    = null;//PDOStatement
    private $error_msg    = '';  //错误信息
    private $debug        = true;//调试
    private $sql_options  = array('table' => '', 'fields' => '', 'where' => '', 'order' => '', 'limit' => '', 'group' => '');
    private $query_sql    = array();//查询语句
    private $num_rows     = 0;      //影响行数或数据集数
    private $insert_id    = 0;      //最后插入id
    
    /**
     * 构造函数
     * 
     * @param string $dbname    数据库名，默认test
     * @param string $username  用户名，默认root
     * @param string $password  密码，默认空
     * @param string $host		MySQL服务器名或ip，默认localhost
     * @param string $engine    数据库引擎，默认mysql
     * @param string $port      端口，默认空
     * @param array  $options   连接属性，仅对mysql引擎有效
     * 
     * @return bool 连接成功，返回true，否则返回false
     */
    function __construct($dbname = 'test', $username = 'root', $password = '', $host = 'localhost', $engine = 'mysql', $port = '', $options = array(PDO::ATTR_PERSISTENT => true)) {
        $dsn = "{$engine}:host={$host};dbname={$dbname};port={$port}";
        
        try {
            $conn = parent::__construct($dsn, $username, $password, $engine == 'mysql' ? $options : null);
            $this->setProperty($this->sql_options)->exec('SET NAMES utf8');
            return true;
        } 
        catch (PDOException $e) {
            return false;
        }
    }
    
    /**
     * 利用__call方法实现一些特殊的方法，如table($table)、where($where)、limit($limit)...
     * 
     * @param string $method  方法名
     * @param string $args    调用参数
     * 
     * @return object 本类实例
     */
    function __call($method, $args) {
        $method     = strtolower($method);
        
        array_key_exists($method, $this->sql_options) && $this->setProperty($method, $args[0]);
        
        return $this;
    }
    
    /**
     * 执行sql查询
     * 
     * @param string $sql	     sql语句
     * @param int    $fetch_type fetch参数
     * 
     * @return mixed 如果成功查询，返回数据组，否则返回false
     */
    function execute($sql = '', $fetch_type = PDO::FETCH_ASSOC) {
        $sql = $sql ? $sql : $this->getSQL();        
        $this->setProperty($this->sql_options);
        $time_start = microtime(true);
        
        if (($this->statement = $this->prepare($sql)) === false) {
            $this->errorHandler('Prepare Failed!');
            return false;
        }
        
        $query = $this->statement->execute();
        
        if ($query === false) {
            
            unset($query);
            
            $this->errorHandler();
            return false;
        }
        
        
        
        $return = $this->fetchAll($fetch_type);
        
        $this->query_count++;
        $this->query_sql[] = array(
            'sql' 		=> $sql,
            'num_rows'  => $this->num_rows,
            'time'	    => $this->execute_time($time_start)
        );
        
        unset($query, $sql);
        
        return $return;
    }
    
    /**
     * 获取所有查询数据
     * 
     * @param int $fetch_type fetch参数
     * 
     * @return array 数据组
     */
    private function fetchAll($fetch_type = PDO::FETCH_ASSOC) {
        $statement = $this->statement;
        $result    = $statement->fetchAll($fetch_type);
        $num_rows  = preg_match('/^\s*SELECT/i', $statement->queryString) ? count($result) : $statement->rowCount();
        $insert_id = preg_match('/^\s*INSERT|REPLACE/i', $statement->queryString) ? $this->lastInsertId() : 0;
        $this->setProperty('num_rows', $num_rows)->setProperty('insert_id', $insert_id);
        
        return $result;
    }
    
    /**
     * 插入
     * 
     * @param mixed  $fields 插入字段或键值对数组(键=列名；值=列值)
     * @param string $values 插入值，仅当$fields为string有效
     * 
     * @return bool 成功插入，返回true，否则返回false
     */
    function insert($fields, $values = '') {
        if (is_string($fields)) {
            $sql    = "INSERT INTO `{$this->table}`({$fields}) VALUES({$values})";
        }
        else {
            $values = join("','", $fields);
            $fields = join('`,`', array_keys($fields));
            $sql    = "INSERT INTO `{$this->table}`(`{$fields}`) VALUES('{$values}')";
        }
        
        return $this->execute($sql);
    }
    
    /**
     * 更新
     * 
     * @param mixed $data 更新字段（值）或键值对数组(键=列名；值=列值)
     * 
     * @return bool 成功更新，返回true，否则返回false
     */
    function update($data) {
        $sql = '';
        
        if (is_string($data)) {
            $sql = $data;
        }
        else {
            
            foreach ($data as $k => $v) {
                $sql .= ", `{$k}`='{$v}'";
            }
            
            $sql = substr($sql, 1);
        }
        
        if (!$where = $this->getWhere()) {//没有where限制，不更新，以避免全表更新
            return false;
        }
        
        $sql = "UPDATE `{$this->table}` SET {$sql}{$where}{$this->getOrder()}{$this->getLimit()}";
        
        return $this->execute($sql);
    }
    
    /**
     * 删除
     * 
     * @return bool 成功删除，返回true，否则返回false
     */
    function delete() {
        
        if (!$where = $this->getWhere()) {//没有where限制，不更新，以避免全表删除
            return false;
        }
        
        $sql = "DELETE FROM {$this->table}{$where}{$this->getOrder()}{$this->getLimit()}";
        
        return $this->execute($sql);
    } 
    
    /**
     * 获取sql语句
     * 
     * @return string sql语句
     */
    private function getSQL() {
        return "SELECT {$this->getFields()} FROM {$this->table}{$this->getWhere()}{$this->getGroup()}{$this->getOrder()}{$this->getLimit()}";
    }
    
    /**
     * 获取选取字段名
     * 
     * @return string 选取字段
     */
    private function getFields() {
        return $this->fields ? $this->fields : '*';
    }
    
    /**
     * 获取where条件
     * 
     * @return string where条件
     */
    private function getWhere() {
        return $this->where ? ' WHERE ' . $this->where : '';
    }
    
    /**
     * 获取排序 
     * 
     * @return string 排序
     */
    private function getOrder() {
        return $this->order ? ' ORDER BY ' . $this->order : '';
    }
    
    /**
     * 获取分组 
     * 
     * @return string 排序
     */
    private function getGroup() {
        return $this->group ? ' ORDER BY ' . $this->group : '';
    }
    
    /**
     * 获取limit
     * 
     * @return string limit
     */
    private function getLimit() {
        return $this->limit ? ' LIMIT ' . $this->limit : '';
    }
    
    /**
     * 设置属性
     * 
     * @param mixed  $property 属性名或属性键值对数组
     * @param string $value    属性值，仅当$property为string时有效
     * 
     * @return object 本类实例
     */
    function setProperty($property, $value = false) {
        
        if (is_string($property)) {
            $this->$property = $value;
        }
        else {
            foreach ($property as $key => $value) {
                $this->$key = $value;
            }
        }
        
        return $this;
    }
    
    /**
     * 获取数性值
     * 
     * @param mixed $property 属性名或包含属性键名的数组
     * 
     * @return mixed 属性值
     */
    function getProperty($property) {
        
        if(is_string($property)) {
            return $this->$property;
        }
        
        $return = array();
        
        foreach ($property as $value) {
            $return[$value] = $this->$value;
        }
        
        unset($property);
        
        return $return;
    }

    /**
     * 返回执行时间，单位：秒
     * 
     * @param int    $time_start 开始时间，默认：START_TIME_MICRO
     * @param string $unit       显示时间文字，默认：s
     * @param int    $precision  小数点精度，默认：6
     * 
     * @return string 执行时间
     */
    private function execute_time($time_start, $unit = 's', $precision = 6) {
        return round(microtime(true) - $time_start, $precision) . $unit;
    } 
    
    /**
     * 错误处理
     * 
     * @param string $error 错误信息
     * 
     * @param bool true
     */
    private function errorHandler($error = '') {
        
        if ($error == '') {
            $error_arr = $this->statement->errorInfo();
            $error     = $error_arr[2];
        }
        
        $error  = "Query Failed: {$error}\nLast SQL: {$this->statement->queryString}\n";
        $this->error_msg = $error;
        
        if ($this->debug) {
            echo $error;
        }
        
        return true;
    }
}
?>